package com.smartplatform.websocket;

import com.smartplatform.event.CheckOnlineEvent;
import com.smartplatform.event.DeviceOnLineEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.Map;

/**
 * @Author 咕唧
 * @Date 2022/6/2 16:38
 * @Version 1.0
 */
@Component
public abstract class MyBaseWebSocketHandler extends TextWebSocketHandler {

    private ApplicationContext context;


    @Autowired
    public void setContext(ApplicationContext context) {
        this.context = context;
    }

    public ApplicationContext getContext() {
        return context;
    }

    /**
     * 当和客户端建立连接后执行
     *
     * @param session 正在建立的会话
     * @throws Exception
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //我们请求的什么地址建立的连接 ,比如/websocket/zhangsan
        String uri = session.getUri().toString();
        //我们约定,最后一个/后的内容就是你是谁
        String deviceId = uri.substring(uri.lastIndexOf("/") + 1);
        //把名字保存到了会话中
        session.getAttributes().put("name", deviceId);
        //如果之前有会话应该先关闭再添加
        WebSocketSession webSocketSession = getAllClient().get(deviceId);
        if (webSocketSession != null && webSocketSession.isOpen()) {
            webSocketSession.close();
        }
        //保存起来
        getAllClient().put(deviceId, session);
        //首次建立连接就相当于收到了一次心跳
        getHeartbeatTime().put(deviceId, System.currentTimeMillis());
        System.err.println("MyBaseWebSocketHandler的afterConnectionEstablished方法执行了");
        //需要更新事件就传递true，将事件发布出去
        if (isNeedUpdateOnline()) {
            DeviceOnLineEvent deviceOnLineEvent = new DeviceOnLineEvent();
            deviceOnLineEvent.setDeviceId(deviceId);
            deviceOnLineEvent.setIp(session.getRemoteAddress().getHostString());
            context.publishEvent(deviceOnLineEvent);
        }

    }

    /**
     * 检查心跳的方法
     *
     * @param session
     * @param message
     * @throws Exception
     */

    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
        super.handlePongMessage(session, message);
    }

    /**
     * 收到消息后执行
     *
     * @param session
     * @param message
     * @throws Exception
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.err.println("MyBaseWebSocketHandler的handleTextMessage方法执行了"+message.getPayloadLength());
        // 获取到当前发送消息的设备的id
        Object deviceId = session.getAttributes().get("name");
        //记录一下发送的时间
        getHeartbeatTime().put((String) deviceId, System.currentTimeMillis());
    }

    /**
     * 关闭连接后执行
     *
     * @param session
     * @param status
     * @throws Exception
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        Object deviceId = session.getAttributes().get("name");
        //移除会话
        getHeartbeatTime().remove(deviceId);
        getAllClient().remove(deviceId);
        //发布事件设置设备为离线状态
        if (isNeedUpdateOnline()) {
            DeviceOnLineEvent deviceOnLineEvent = new DeviceOnLineEvent();
            deviceOnLineEvent.setDeviceId(deviceId.toString());
            context.publishEvent(deviceOnLineEvent);
        }

    }

    /**
     * 检查心跳，和设备约定多长时间没有发送指令就视为设备不在线
     * 把设备更新为离线状态
     *
     * @param event
     */
    @Async
    @EventListener
    public void onEvent(CheckOnlineEvent event) {
        System.err.println("收到了发送的命令的onEvent的检查心跳的方法执行了");
        //遍历集合中所有的设备
        getHeartbeatTime().entrySet().forEach(entry -> {
            //获取设备id和上上次发送指令时间
            String deviceId = entry.getKey();
            Long lastHeartTime = entry.getValue();
            if (System.currentTimeMillis() - lastHeartTime >= 60000) {
                //认为你设备离线，关闭会话
                WebSocketSession webSocketSession = getAllClient().get(deviceId);
                if (webSocketSession != null && webSocketSession.isOpen()) {
                    try {
                        //关闭会话
                        webSocketSession.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

    }

    /**
     * 钩子方法,用于判断是不是需要更新设备的在线状态
     * 钩子方法，父类声明，父类调用，但是由子类实现的方法
     *
     * @return
     */
    public abstract boolean isNeedUpdateOnline();

    //存放更新时间的map
    protected abstract Map<String, Long> getHeartbeatTime();

    //存放连接的map
    protected abstract Map<String, WebSocketSession> getAllClient();

}
